/*
* All code is only for jiecai 
* All rights reserved
*/
package com.jc.common.redis;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.apache.log4j.Logger;

import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.Response;
import redis.clients.jedis.Transaction;
import redis.clients.util.SafeEncoder;

import com.jc.common.redis.trans.RedisTransactionStatus;
import com.jc.common.util.RedisUtil;

/**
 *
 * @author Kenny
 * created on 2011-5-12
 */
public class RedisTemplate {

	private static final Logger log = Logger.getLogger(RedisTemplate.class);
	
	private JedisPool redisPool;
	
	// -------------------------基础操作-------------------------
	public Long delKey(String... key) {
		Jedis redis = null;
		try {
			redis = getJedis();
			return redis.del(key);
		} catch (Exception e) {
			returnBrokenJedis(redis);
			redis = null;
			throw new MyRedisException("Deleting key failed: " + e.getMessage(), e);
		} finally {
			if (null != redis) {
				returnJedis(redis);
			}
		}
	}
	public Boolean exists(String key) {
		Jedis redis = null;
		try {
			redis = getJedis();
			return redis.exists(key);
		} catch (Exception e) {
			returnBrokenJedis(redis);
			redis = null;
			throw new MyRedisException(e.getMessage(), e);
		} finally {
			if (null != redis) {
				returnJedis(redis);
			}
		}
	}
	public String rename(String oldkey, String newKey) {
		Jedis redis = null;
		try {
			redis = getJedis();
			return redis.rename(oldkey, newKey);
		} catch (Exception e) {
			returnBrokenJedis(redis);
			redis = null;
			throw new MyRedisException(e.getMessage(), e);
		} finally {
			if (null != redis) {
				returnJedis(redis);
			}
		}
	}
	public Long inc(String key) {
		Jedis redis = null;
		try {
			redis = getJedis();
			return redis.incr(key);
		} catch (Exception e) {
			returnBrokenJedis(redis);
			redis = null;
			throw new MyRedisException(e.getMessage(), e);
		} finally {
			if (null != redis) {
				returnJedis(redis);
			}
		}
	}
	
	public Long decr(String key) {
		Jedis redis = null;
		try {
			redis = getJedis();
			return redis.decr(key);
		} catch (Exception e) {
			returnBrokenJedis(redis);
			redis = null;
			throw new MyRedisException(e.getMessage(), e);
		} finally {
			if (null != redis) {
				returnJedis(redis);
			}
		}
	}
	
	public Set<String> keys(String pattern) {
		Jedis redis = null;
		try {
			redis = getJedis();
			return redis.keys(pattern);
			
		} catch (Exception e) {
			returnBrokenJedis(redis);
			redis = null;
			throw new MyRedisException("Deleting key failed: " + e.getMessage(), e);
		} finally {
			if (null != redis) {
				returnJedis(redis);
			}
		}
	}
	//--------------------------基础操作结束-----------------------
	
	// -------------------------list操作 start-------------------------
	public <T extends Serializable> Long lpush(String key, T v) {
		Jedis redis = null;
		try {
			redis = getJedis();
			return redis.lpush(getKeyByte(key), RedisUtil.encode(v));
		} catch (Exception e) {
			returnBrokenJedis(redis);
			redis = null;
			throw new MyRedisException(e.getMessage(), e);
		} finally {
			if (null != redis) {
				returnJedis(redis);
			}
		}
	}
	
	public <T extends Serializable> Long rpush(String key, T v) {
		Jedis redis = null;
		try {
			redis = getJedis();
			return redis.rpush(getKeyByte(key), RedisUtil.encode(v));
		} catch (Exception e) {
			returnBrokenJedis(redis);
			redis = null;
			throw new MyRedisException(e.getMessage(), e);
		} finally {
			if (null != redis) {
				returnJedis(redis);
			}
		}
	}
	
	
	public <T extends Serializable> Long sadd(String key, T v) {
		Jedis redis = null;
		try {
			redis = getJedis();
			return redis.sadd(getKeyByte(key), RedisUtil.encode(v));
		} catch (Exception e) {
			returnBrokenJedis(redis);
			redis = null;
			throw new MyRedisException(e.getMessage(), e);
		} finally {
			if (null != redis) {
				returnJedis(redis);
			}
		}
	}
	
	@SuppressWarnings("unchecked")
	public <T extends Serializable> T spopPojo(String key) {
		Jedis redis = null;
		try {
			redis = getJedis();
			byte[] result = redis.spop(getKeyByte(key));
			if (result != null && result.length > 0) {
				return (T)RedisUtil.decode(result);
			} else {
				return null;
			}
		} catch (Exception e) {
			returnBrokenJedis(redis);
			redis = null;
			throw new MyRedisException(e.getMessage(), e);
		} finally {
			if (null != redis) {
				returnJedis(redis);
			}
		}
	}
	
	
	@SuppressWarnings("unchecked")
	public <T extends Serializable> List<T> lrangePojo(String key, int start, int end) {
		Jedis redis = null;
		try {
			redis = getJedis();
			List<byte[]> result = redis.lrange(getKeyByte(key), start, end);
			if (result != null && result.size() > 0) {
				return (List<T>)RedisUtil.decode(result);
			} else {
				return null;
			}
		} catch (Exception e) {
			returnBrokenJedis(redis);
			redis = null;
			throw new MyRedisException(e.getMessage(), e);
		} finally {
			if (null != redis) {
				returnJedis(redis);
			}
		}
	}
	@SuppressWarnings("unchecked")
	public <T extends Serializable> T lpopPojo(String key) {
		Jedis redis = null;
		try {
			redis = getJedis();
			byte[] result = redis.lpop(getKeyByte(key));
			if (result != null && result.length > 0) {
				return (T)RedisUtil.decode(result);
			} else {
				return null;
			}
		} catch (Exception e) {
			returnBrokenJedis(redis);
			redis = null;
			throw new MyRedisException(e.getMessage(), e);
		} finally {
			if (null != redis) {
				returnJedis(redis);
			}
		}
	}
	public <T extends Serializable> Long llen(String key) {
		Jedis redis = null;
		try {
			redis = getJedis();
			return redis.llen(key);
		} catch (Exception e) {
			returnBrokenJedis(redis);
			redis = null;
			throw new MyRedisException(e.getMessage(), e);
		} finally {
			if (null != redis) {
				returnJedis(redis);
			}
		}
	}
	// -------------------------list操作 end-------------------------
	
	// -------------------------hash操作 start-------------------------
	public <T extends Serializable> Long hset(String key, String filed,  T v) {
		Jedis redis = null;
		try {
			redis = getJedis();
			return redis.hset(getKeyByte(key),getKeyByte(filed),RedisUtil.encode(v));
			
		} catch (Exception e) {
			returnBrokenJedis(redis);
			redis = null;
			throw new MyRedisException(e.getMessage(), e);
		} finally {
			if (null != redis) {
				returnJedis(redis);
			}
		}
	}
	
	public  List<String> hkeys(String key) {
		Jedis redis = null;
		List<String> keys = new ArrayList<String>();
		try {
			redis = getJedis();
			Set<byte[]> keySet = redis.hkeys(getKeyByte(key));
			Iterator<byte[]> it = keySet.iterator();
			while (it.hasNext()) {
				keys.add(SafeEncoder.encode(it.next()));
			}
			
		} catch (Exception e) {
			returnBrokenJedis(redis);
			redis = null;
			throw new MyRedisException(e.getMessage(), e);
		} finally {
			if (null != redis) {
				returnJedis(redis);
			}
		}
		return keys;
	}
	
	
	@SuppressWarnings("unchecked")
	public <T extends Serializable> T hget(String key, String filed) {
		Jedis redis = null;
		try {
			redis = getJedis();
			byte[] result = redis.hget(getKeyByte(key),getKeyByte(filed));
			if (result != null && result.length > 0) {
				return (T)RedisUtil.decode(result);
			} else {
				return null;
			}
		} catch (Exception e) {
			returnBrokenJedis(redis);
			redis = null;
			throw new MyRedisException(e.getMessage(), e);
		} finally {
			if (null != redis) {
				returnJedis(redis);
			}
		}
	}
	// -------------------------hash操作 end-------------------------
	
	// -------------------------POJO操作-------------------------
	public <T extends Serializable> String setPojo(String key, T value) {
		Jedis redis = null;
		try {
			redis = getJedis();
			return redis.set(getKeyByte(key), RedisUtil.encode(value));
		} catch (Exception e) {
			returnBrokenJedis(redis);
			redis = null;
			throw new MyRedisException(e.getMessage(), e);
		} finally {
			if (null != redis) {
				returnJedis(redis);
			}
		}
	}
	
	public <T extends Serializable> String setExPojo(String key, int seconds, T value) {
		Jedis redis = null;
		try {
			redis = getJedis();
			return redis.setex(getKeyByte(key), seconds, RedisUtil.encode(value));
		} catch (Exception e) {
			returnBrokenJedis(redis);
			redis = null;
			throw new MyRedisException(e.getMessage(), e);
		} finally {
			if (null != redis) {
				returnJedis(redis);
			}
		}
	}

	public <T extends Serializable> Long setnxPojo(String key, T value) {
		Jedis redis = null;
		try {
			redis = getJedis();
			return redis.setnx(getKeyByte(key), RedisUtil.encode(value));
		} catch (Exception e) {
			returnBrokenJedis(redis);
			redis = null;
			throw new MyRedisException(e.getMessage(), e);
		} finally {
			if (null != redis) {
				returnJedis(redis);
			}
		}
	}

	/**
	 * This method need some testing.
	 */
	public <T extends Serializable> void mSetPojo(Map<String, T> keyValueMap) {
		
		Jedis redis = null;
		if (keyValueMap == null || keyValueMap.size() == 0) {
			throw new MyRedisException("There is no data in map.");
		}
		try {
			redis = getJedis();
			byte[][] data = RedisUtil.encode(keyValueMap);
			redis.mset(data);
		} catch (Exception e) {
			returnBrokenJedis(redis);
			redis = null;
			throw new MyRedisException(e.getMessage(), e);
		} finally {
			if (null != redis) {
				returnJedis(redis);
			}
		}
	}
	/**
	 * This method need some testing.
	 */
	public <T extends Serializable> Long mSetnxPojo(
			Map<String, T> keyValueMap) {
		
		if (keyValueMap == null || keyValueMap.size() == 0) {
			throw new MyRedisException("There is no data in map.");
		}
		
		Jedis redis = null;
		try {
			redis = getJedis();
			byte[][] data = RedisUtil.encode(keyValueMap);
			return redis.msetnx(data);
		} catch (Exception e) {
			returnBrokenJedis(redis);
			redis = null;
			throw new MyRedisException(e.getMessage(), e);
		} finally {
			if (null != redis) {
				returnJedis(redis);
			}
		}
	}

	@SuppressWarnings("unchecked")
	public <T extends Serializable> T getPojo(String key) {
		Jedis redis = null;
		try {
			redis = getJedis();
			byte[] value = redis.get(getKeyByte(key));
			if (null == value) {
				return null;
			} else {
				return (T) RedisUtil.decode(value);
			}
		} catch (Exception e) {
			returnBrokenJedis(redis);
			redis = null;
			throw new MyRedisException(e.getMessage(), e);
		} finally {
			if (null != redis) {
				returnJedis(redis);
			}
		}
	}
	
	public  String get(String key) {
		Jedis redis = null;
		try {
			redis = getJedis();
			String value = redis.get(key);
			if (null == value) {
				return null;
			} else {
				return value;
			}
		} catch (Exception e) {
			returnBrokenJedis(redis);
			redis = null;
			throw new MyRedisException(e.getMessage(), e);
		} finally {
			if (null != redis) {
				returnJedis(redis);
			}
		}
	}
	
	public String set(String key, String value) {
		Jedis redis = null;
		try {
			redis = getJedis();
			return redis.set(key, value);
		} catch (Exception e) {
			returnBrokenJedis(redis);
			redis = null;
			throw new MyRedisException(e.getMessage(), e);
		} finally {
			if (null != redis) {
				returnJedis(redis);
			}
		}
	}
	public String setex(String key, int seconds, String value) {
		Jedis redis = null;
		try {
			redis = getJedis();
			return redis.setex(key, seconds, value);
		} catch (Exception e) {
			returnBrokenJedis(redis);
			redis = null;
			throw new MyRedisException(e.getMessage(), e);
		} finally {
			if (null != redis) {
				returnJedis(redis);
			}
		}
	}


	@SuppressWarnings("unchecked")
	public <T extends Serializable> List<T> mGetPojo(String... keys) {
		byte[][] bkeys = new byte[keys.length][];
        for (int i = 0; i < bkeys.length; i++) {
            bkeys[i] = SafeEncoder.encode(keys[i]);
        }
		Jedis redis = null;
		try {
			redis = getJedis();
			List<byte[]> result = redis.mget(bkeys);
			if (result == null || result.size() == 0) {
				return null;
			} else {
				return (List<T>)RedisUtil.decode(result);
			}
		} catch (Exception e) {
			returnBrokenJedis(redis);
			redis = null;
			throw new MyRedisException(e.getMessage(), e);
		} finally {
			if (null != redis) {
				returnJedis(redis);
			}
		}
	}
	// -------------------------POJO操作 end-------------------------
	
	public List<byte[]> lrangeBytes(String key, int start, int end) {
		Jedis redis = null;
		try {
			redis = getJedis();
			return redis.lrange(getKeyByte(key), start, end);
		} catch (Exception e) {
			returnBrokenJedis(redis);
			redis = null;
			throw new MyRedisException(e.getMessage(), e);
		} finally {
			if (null != redis) {
				returnJedis(redis);
			}
		}
	}
	
	// -------------------------事务操作 begin-------------------------
	
	public Jedis watch(String... keys){
		Jedis redis = null;
		try {
			redis = getJedis();
			redis.watch(keys);
			return redis;
		} catch (Exception e) {
			returnBrokenJedis(redis);
			throw new MyRedisException(e.getMessage(), e);
		}
	}
	
	public String unWatch(Jedis redis){
		if (redis == null){
			return null;
		}
		
		try {
			return redis.unwatch();
		} catch (Exception e) {
			returnBrokenJedis(redis);
			redis = null;
			throw new MyRedisException(e.getMessage(), e);
		} finally {
			if (null != redis) {
				returnJedis(redis);
			}
		}
	}
	
	public Transaction mutil(Jedis redis){
		if (redis == null){
			return null;
		}
		return redis.multi();
	}
	
	public <T extends Serializable> Long sadd(String key, T v, RedisTransactionStatus ts) {
		try {
			Response<Long> resp = ts.getTransaction().sadd(getKeyByte(key), RedisUtil.encode(v));
			if (resp == null){
				return -1L;
			}
			return resp.get();
		} catch (Exception e) {
			throw new MyRedisException(e.getMessage(), e);
		} 
	}
	/**
	 * This method need testing.
	 */
	public <T extends Serializable> String setPojo(String key, T v,RedisTransactionStatus ts) {
		try {
			Response<String> resp = ts.getTransaction().set(getKeyByte(key), RedisUtil.encode(v));
			if (resp == null){
				return null;
			}
			return resp.get();
		} catch (Exception e) {
			throw new MyRedisException(e.getMessage(), e);
		} 
	}
	
	
	// -------------------------事务操作 end-------------------------
	private byte[] getKeyByte(String key) {
		return SafeEncoder.encode(key);
	}
	public Jedis getJedis() throws MyRedisException {
		if (null != redisPool) {
			try {
				return redisPool.getResource();
			} catch (Exception e) {
				log.error(e.getMessage(), e);
				throw new MyRedisException("Can't retrieve resource from pool", e);
			}
		}
		return null;
	}
	
	private void returnJedis(Jedis redis) {
		if (null != redisPool) {
			try {
				redisPool.returnResource(redis);
			} catch (Exception e) {
				log.error(e.getMessage(), e);
			}
		}
	}
	private void returnBrokenJedis(Jedis redis) {
		if (null != redisPool) {
			try {
				redisPool.returnBrokenResource(redis);
			} catch (Exception e) {
				log.error(e.getMessage(), e);
			}
		}
	}
	public JedisPool getRedisPool() {
		return redisPool;
	}
	public void setRedisPool(JedisPool redisPool) {
		this.redisPool = redisPool;
	}
}
